/**
 * gislin 2017年7月13日
 */
package com.btcode.orm.executer;

import com.btcode.common.StringUtil;
import com.btcode.db.executer.IDataExecuter;
import com.btcode.db.result.PageResult;
import com.btcode.db.result.Row;
import com.btcode.db.sqlparameter.PageInfo;
import com.btcode.db.sqlparameter.SQLParameterList;
import com.btcode.db.wherefilter.WhereFilterType;
import com.btcode.exception.BusinessException;
import com.btcode.exception.MsgException;
import com.btcode.orm.cache.IORMCacheExecuter;
import com.btcode.orm.conver.IModelConver;
import com.btcode.orm.conver.IValueConver;
import com.btcode.orm.exception.PrimarykeyEmptyException;
import com.btcode.orm.exception.PrimarykeyMissException;
import com.btcode.orm.filter.FilterSingle;
import com.btcode.orm.filter.IFilter;
import com.btcode.orm.model.FieldValue;
import com.btcode.orm.model.IDomainField;
import com.btcode.orm.model.IField;
import com.btcode.orm.model.IModel;
import com.btcode.orm.model.IModelAddition;
import com.btcode.orm.model.ModelPageResult;
import com.btcode.orm.relation.ex.IRelation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <pre>
 * 缓存设计思考：
 * 缓存的难点在于：
 * 1、如何缓存
 * 2、缓存键的设计
 * 3、缓存更新
 * 4、关联关系缓存
 * 5、关联关系更新
 *
 * 方案：
 *
 * 1、目前，默认使用Ehcache，通过接口，可以更换中间件，比如使用Redis
 *
 * 2、缓存键是获取到缓存的依据
 * 		a.比如根据表名+主键值，获取缓存
 * 		b.或者提供自定义缓存键，比如字典项的缓存，就必须通过自定义来实现（字典类型+字典项代码）
 *
 * 3、只有在update和delete的时候，才需要对缓存进行更新，更新的做法就是删除缓存，等下一次getModel的时候，再缓存进来，以此保证数据一致。
 * 		a.如果是通过主键更新，就比较简单，因为主键是不允许被修改的，通过主键更新缓存，一定会成功
 * 		b.但如果是通过自定义缓存键更新的话，就稍微复杂，因为也许更新的时候，就是更新自定义缓存键。
 * 			考虑update(model)方法，传进来的model中，可能就是把自定义缓存键的字段给更新了，如此一来，再通过新的model是无法定位到缓存的。
 *
 * 		根据上述两点，在更新缓存的时候，为了简单起见，先getModel,而且这里的model必须有主键，再根据获取到的model去删除缓存，以此确保缓存被更新
 *
 * 4、关联关系缓存，关联关系分为一对多，多对多，其中一对多是一张主表对一张从表，多对多是两张主表，通过一张关联表进行关联。
 * 		这里明确一个概念：直属关联
 * 			在一对多的关联中，主表和从表，就是直属关联。
 * 			在多对多的关联中，主表1和关联表，主表2和关联表，就是直属关联，而主表1和主表2就不是直属关联。
 * 		在缓存关联关系的时候，只缓存直属关联。注意，这里缓存的是关联，而不是最终关联到的从表，这是有区别的。
 * 			在一对一里
 * 				比如公司员工和其办公用品，在缓存的时候，公司员工通过"COM_USER@_用户主键值"作为键进行缓存，而其办公用品的关联，通过"COM_THING@_COM_USER@_用户主键值"作为键值进行缓存。缓存的内容就是用品的主键值集合。
 * 				同时，办公用品，本身也会做缓存，通过"COM_THING@_用品主键值"作为键进行缓存。
 * 				在这个基础上，当获取一个用户的信息时，首先通过用户主键，也就是"COM_USER@_用户主键值"，拿到用户的缓存，在通过用户和用品的关联,也就是"COM_THING@_COM_USER@_用户主键值"，拿到用户用品关联关系。
 * 				再通过关联关系中的用品主键值，循环获取真正的用品缓存。
 * 				这样设计的好处是，用品可以自由更新，而不影响用户用品的关联缓存，但这里会有个问题，加入用品被删除了，而用户用品的关联关系在缓存中还存在，这个时候怎么办？这个问题后面会给出方案。
 * 			在多对多里
 * 				比如学生和老师的关联关系，在缓存的时候，学生通过"STUDENT@_学生主键值",作为主键进行缓存，老师通过"TEACHER@_老师主键值"，作为主键进行缓存。
 * 				而关联关系，分两个存，学生关联的老师，通过"TEACHER@_STUDENT@_学生主键值"，进行缓存，缓存的是对应老师的主键值集合。老师关联的学生，通过"STUDENT@_TEACHER@_老师主键值",进行缓存，缓存的是学生的主键值集合。
 * 				设计原因同一对一。
 * 		通过直属关联的缓存，可以拿到另一张主表的主键集合，通过这个主键集合，再循环通过getModel去获取数据，以此获取到完整的关联数据。
 * 		如果另一张主表也做了缓存，那么getModel的时候，就不用传透到数据库层了。以此提高速度。
 *
 * 5、关联关系的更新
 * 		关联关系更新，分为两方面
 * 		A.主表记录删除
 * 		B.主从关联修改
 * 		C.从表记录删除
 * 		D.从表记录修改
 *
 * 		A.主表记录删除
 * 			主表记录删除，那么关联关系肯定要删除，但是所关联的从表记录，是否要删除呢？引出下面两个概念：强关联，弱关联。
 * 				强关联意味着，主表的记录若不在了，那么从表的记录将没有存在的意义，也要被删除。
 * 				弱关联意味着，主表记录的删除与否，不直接影响从表的记录。
 * 			多对多：
 * 				在多对多的关系中，可以分解成两个一对多关系，比如学生和老师的关系，一个学生可以对多个老师，一个老师也可以对多个学生。
 * 				那么学生和老师就是多对多的关系，他们两者需要一张关系表进行关联，如果一个学生不存在了，那么关系表中和这个学生相关的记录也就没有意义，需要被删除
 * 				反过来，老师也一样。
 * 				所以，在多对多的关系中，主表和关联表的关系，就是强关联关系。
 * 			一对多：
 * 				在一对多的关系中，有些场景是强关联，而有些则是弱关联。
 * 				比如，公司员工和公司用品的关系，一个员工会配备多个办公用品，比如笔记本，比如椅子桌子，但是当员工离职的时候，这些用品是不会消失的。这种关系就是弱关联。
 * 				比如，公司和公司部门的关系，一个公司会有多个部门，如果一个公司消失了，那么这些部门也就不再存在，这就是强关联。
 * 			所以，对于一对一：如果是强关联，第一步，删除主表缓存，删除主表数据库记录，第二步，删除关联关系缓存，第三步，删除从表数据
 * 							  如果是弱关联，第一步，删除主表缓存，删除主表数据库记录，第二步，删除关联关系缓存
 * 				 对于多对多，第一步，删除主表缓存，删除主表数据库记录，第二步，删除关联关系缓存，第三步，删除关联关系表数据库记录
 *
 * 		B.主从关联修改
 * 			对于多对多，处理起来比较简单
 * 				比如学生和老师，如果学生所关联的老师们变了，先删除"TEACHER_STUDENT_学生主键值"这个关联关系缓存，然后再删除数据库关联表里，和这个学生有关的记录。最后再把新数据插入。
 * 			对于一对多，比较复杂，但复杂不在缓存处理方面，因为缓存也是如多对多那般，直接删除即可。麻烦的是在数据库段的处理。
 * 				比如员工和公司用品，当员工的公司用品发生变化，那么不能直接把公司用品记录删除，再插入，而是要做比对对于被移除的用品，要把和员工关联的字段置空，而把新关联进来的用品记录的关联字段设置上当前员工的主键值。
 *
 * 		C.从表记录删除
 * 			这种情况，不是由主表发起，delete(model)的时候，这个model是从表的model，这个时候，如何使主表和该从表的关联也相应更新呢？在第4点的关联关系缓存中的键设计已经兼顾到了这点。
 * 			一个表的记录发生变化，除了移除自己身的缓存，也要所有与其相关的关联缓存，如何删除？ehcache提供了模糊删除功能，而关联关系的键名是以从表的表名开头，这就给模糊删除提供了可能，
 *
 * 		D.同C的操作
 *
 *
 * 关联关系的缓存，
 * </pre>
 *
 * @author gislin 2017年7月13日
 */
public class DefaultORMExecuter implements IORMExecuter {

    protected IDataExecuter dataExecuter;
    protected IModelConver modelConver;
    protected IValueConver valueConver;
    protected IORMCacheExecuter ormCache;

    /**
     * @param dataExecuter 用于执行sql
     * @param modelConver 用户把resultSet转换成model
     * @param ormCache 缓存工具
     * @param valueConver 值转换器
     */
    public DefaultORMExecuter(IDataExecuter dataExecuter, IModelConver modelConver,
        IORMCacheExecuter ormCache, IValueConver valueConver) {
        this.dataExecuter = dataExecuter;
        this.modelConver = modelConver;
        this.ormCache = ormCache;
        this.valueConver = valueConver;
    }

    @Override
    public <T extends IModel> void create(T model) {
        try {
            dataExecuter.beginTransaction();
            createPureModel(model);
            updateRelationModels(model);
            dataExecuter.commit();
        }
        catch (Exception e) {
            dataExecuter.rollback();
            if (e instanceof BusinessException) {
                throw e;
            }
            throw new MsgException("新建出错", e);
        }
    }

    /**
     * 批量新增不支持关联数据插入
     */
    @Override
    @SuppressWarnings({"unchecked"})
    public <T extends IModel> void creates(List<T> models) {

        if (models == null || models.size() <= 0) {
            return;
        }

        dataExecuter.beginTransaction();

        try {

            List<SQLParameterList> sps = new ArrayList<>();

            for (T model : models) {

                SQLParameterList sqlParam = buildCreateParameter(model);

                IModelAddition<T> modelAddition = (IModelAddition<T>) model.getModelAddition();

                if (modelAddition != null) {
                    modelAddition.beforeCreate(model);
                }

                sps.add(sqlParam);
            }
            /**
             * 这一句的执行顺序必须放在最后，因为上面的model在获取参数的时候，会变化
             */
            String sql = buildCreateSQL(models.get(0));

            dataExecuter.executeUpdateBatch(sql, sps);

            dataExecuter.commit();

            /**
             * 缓存model
             */
            if (models.get(0).isNeedCache()) {
                for (IModel model : models) {
                    ormCache.cacheModel(model);
                }
            }

        }
        catch (Exception e) {
            dataExecuter.rollback();
            if (e instanceof BusinessException) {
                throw e;
            }
            throw new RuntimeException(e);
        }

    }

    /**
     * 获取关联model的功能暂时屏蔽，针对关联model缓存方面的处理有问题
     */
    @Override
    public <T extends IModel> T getModel(T model) {
        T resultModel = getModel(model, true);
        return resultModel;
    }

    @Override
    public <T extends IModel> T getModelWithOutRelation(T model) {
        T resultModel = getPureModel(model, false);
        return resultModel;
    }

    /**
     * @param model 要获取的model，主键必须不为空
     * @param getRelation 是否要获取关联model
     */
    private <T extends IModel> T getModel(T model, boolean getRelation) {
        T resultModel = getPureModel(model, true);

        if (resultModel == null) {
            return null;
        }

        if (getRelation) {
            HashMap<String, List<IModel>> relationModels = getRelationModels(resultModel);
            if (relationModels != null) {
                resultModel.setRelationModels(relationModels);
            }
        }

        return resultModel;
    }

    @Override
    public <T extends IModel> List<T> getModels(T model) {
        if (model == null) {
            return null;
        }

        List<IField> fields = model.getFields();
        List<IFilter> filters = new ArrayList<IFilter>();

        for (IField field : fields) {
            Object value = model.getFieldValue(field.getFieldName());

            if (value != null) {
                IFilter filter = new FilterSingle(field, WhereFilterType.EQUAL, value);
                filters.add(filter);
            }
        }

        if (filters.size() == 0) {
            return null;
        }

        List models = getModelList(model.getClass(), filters, null).getData();

        return models;
    }

    @Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public <T extends IModel> void update(T model) {
        try {
            dataExecuter.beginTransaction();
            updatePureModel(model);
            updateRelationModels(model);

            IModelAddition modelAddition = model.getModelAddition();

            if (modelAddition != null) {
                modelAddition.afterUpdate(model);
            }

            dataExecuter.commit();
        }
        catch (Exception e) {
            dataExecuter.rollback();
            if (e instanceof BusinessException) {
                throw e;
            }
            throw new MsgException("更新出错", e);
        }
    }

    @Override
    public <T extends IModel> void delete(T model) {
        try {
            dataExecuter.beginTransaction();
            /**
             * 先获取一次，因为经过了deletePureModel操作，deleteRelationsModel将失去数据支撑，所以要传进去
             */
            IModel sourceModel = getModel(model);
            deletePureModel(model);
            deleteRelationsModel(sourceModel);
            dataExecuter.commit();
        }
        catch (Exception e) {
            dataExecuter.rollback();
            if (e instanceof BusinessException) {
                throw (BusinessException) e;
            }
            else {
                throw new MsgException("删除出错", e);
            }
        }
    }

    @Override
    public <T extends IModel> ModelPageResult getModelList(Class<T> modelClass,
        List<IFilter> whereFilters,PageInfo pageInfo) {
        return getModelList(modelClass,whereFilters,null,pageInfo);
    }

    @Override
    public <T extends IModel> ModelPageResult getModelList(Class<T> modelClass,
        List<IFilter> whereFilters,String orderString,PageInfo pageInfo) {

        if (whereFilters == null) {
            whereFilters = new ArrayList<>();
        }

        if(!checkOrderString(orderString)){
            throw new MsgException("order字符串不合法");
        }

        if(orderString != null ){
            orderString = " ORDER BY " + orderString;
        }
        else{
            orderString = "";
        }

        T model;
        try {
            model = modelClass.newInstance();
        }
        catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        StringBuffer strBuffer = new StringBuffer(
            " SELECT * FROM " + model.getTableName() + " WHERE 1=1 ");
        SQLParameterList parameterList = new SQLParameterList();

        for (IFilter filter : whereFilters) {

            if (filter == null) {
                continue;
            }

            strBuffer.append(" AND " + filter.getWhereFilter().getWhereText());
            parameterList.putAll(filter.getWhereFilter().getParameterList());
        }

        ModelPageResult modelPageResult;

        if(pageInfo != null){
            PageResult result = dataExecuter.queryListPage(strBuffer.toString() + orderString, parameterList,pageInfo);
            @SuppressWarnings("unchecked") List<T> models = (List<T>) modelConver.converToORMModels(
                result.getData(), model.getClass());

            /**
             * 翻译域信息
             */
            models = converDomainMulit(models);

            modelPageResult = new ModelPageResult(result.getPageInfo(),models);
        }
        else{
            List<Row> rows = dataExecuter.queryList(strBuffer.toString() + orderString, parameterList);
            @SuppressWarnings("unchecked") List<T> models = (List<T>) modelConver.converToORMModels(
                rows, model.getClass());

            /**
             * 翻译域信息
             */
            models = converDomainMulit(models);

            modelPageResult = new ModelPageResult(null,models);
        }
        return modelPageResult;
    }

    /**
     * 验证order字符串，是否合法
     * @param orderString
     * @return
     */
    public boolean checkOrderString(String orderString){
        String regEx = "^[A-Za-z]([A-Za-z0-9_])*[\\.]*([A-Za-z0-9_])*[\\s]*[a-zA-Z]*$";
        Pattern pattern = Pattern.compile(regEx);

        if(StringUtil.isEmpty(orderString)){
            return true;
        }

        String[] orders = orderString.split(",");

        for(String order:orders){
            Matcher matcher = pattern.matcher(order);
            if(!matcher.matches()){
                return false;
            }
        }
        return true;
    }

    /**
     * 删除纯model不包含关联表 gislin 2017年8月25日
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private <T extends IModel> void deletePureModel(T model) {

        if (model == null) {
            return;
        }
        /**
         * 主键不能为空
         */
        checkModelPrimarykeyIsMissOrEmpty(model);

        IModelAddition modelAddition = model.getModelAddition();

        if (modelAddition != null) {
            modelAddition.beforeDelete(model);
        }

        List<IField> keyField = model.getKeyFields();

        StringBuffer strBuffer = new StringBuffer(
            " DELETE FROM " + model.getTableName() + " WHERE ");
        SQLParameterList parameters = new SQLParameterList();

        for (int i = 0; i < keyField.size(); i++) {

            IField field = keyField.get(i);
            Object value = model.getFieldValue(field.getFieldName());

            strBuffer.append(getFieldNameForSQL(field) + " = @" + field.getFieldName());
            parameters.put(field.getFieldName(), value);

            if (i < keyField.size() - 1) {
                strBuffer.append(" AND ");
            }
        }

        dataExecuter.delete(strBuffer.toString(), parameters);

        if (model.isNeedCache()) {
            ormCache.deleteModelCache(model);
        }

        if (modelAddition != null) {
            modelAddition.afterDelete(model);
        }
    }

    /**
     * 删除model的关联表数据 gislin 2017年8月25日
     */
    private <T extends IModel> void deleteRelationsModel(T sourceModel)
        throws InstantiationException, IllegalAccessException {
        List<IRelation> relations = sourceModel.getRelations();

        for (IRelation relation : relations) {

            /**
             * 主表外键字段
             */
            IField masterField = relation.getMasterField();
            /**
             * 从表关联字段
             */
            IField slaveField = relation.getSlaveField();

            List<IModel> slaveModels = sourceModel.getRelationModel(slaveField.getTableName());

            /**
             * 如果关联数据为空，则不做处理
             */
            if (slaveModels == null || slaveModels.size() < 1) {
                continue;
            }

            if (relation.isPrimaryRelationCache()) {
                /**
                 * 删除关联关系缓存
                 */
                ormCache.deleteRelationCacheModels(sourceModel, relation);
            }

            /**
             * 主表外键字段的值
             */
            Object keyValue = sourceModel.getFieldValue(masterField.getFieldName());

            /**
             * 如果是多对多关联就简单处理，先删除后插入
             */
            if (relation.isManyToMany()) {
                String deleteSQL =
                    " DELETE FROM " + slaveField.getTableName() + " WHERE " + slaveField
                        .getFieldName() + "=@" + slaveField.getFieldName();
                SQLParameterList pc = new SQLParameterList();
                pc.put(slaveField.getFieldName(), keyValue);
                dataExecuter.delete(deleteSQL, pc);
            }
            else {

                /**
                 * 清空从表的缓存
                 */
                if (slaveModels.get(0).isNeedCache()) {
                    for (IModel slaveModel : slaveModels) {
                        ormCache.deleteModelCache(slaveModel);
                    }
                }
                if (!relation.isRelationDelete()) {
                    /**
                     * 把从表中相关的外键都置空
                     */
                    String updateSQL =
                        " UPDATE " + slaveField.getTableName() + " SET " + slaveField.getFieldName()
                            + "=null WHERE " + slaveField.getFieldName() + "=@" + slaveField
                            .getFieldName();
                    SQLParameterList pc = new SQLParameterList();
                    pc.put(slaveField.getFieldName(), keyValue);
                    dataExecuter.update(updateSQL, pc);
                }
                else {
                    String deleteSQL =
                        " DELETE FROM " + slaveField.getTableName() + " WHERE " + slaveField
                            .getFieldName() + "=@" + slaveField.getFieldName();
                    SQLParameterList pc = new SQLParameterList();
                    pc.put(slaveField.getFieldName(), keyValue);
                    dataExecuter.delete(deleteSQL, pc);
                }
            }
        }
    }

    /**
     * 获取model的关联model gislin 2017年7月19日
     */
    private <T extends IModel> HashMap<String, List<IModel>> getRelationModels(T model) {

        try {
            List<IRelation> relations = model.getRelations();

            HashMap<String, List<IModel>> relationModelsMap = new HashMap<>();

            for (IRelation relation : relations) {

                List<IModel> slaveModels = null;

                /**
                 * 是否缓存
                 */
                if (relation.isPrimaryRelationCache()) {
                    slaveModels = ormCache.getRelationModels(model, relation);
                }

                /**
                 * 如果缓存为空，传透到数据库
                 */
                if (slaveModels == null) {

                    /**
                     * 主表外键字段
                     */
                    IField masterField = relation.getMasterField();
                    /**
                     * 从表关联字段
                     */
                    IField slaveField = relation.getSlaveField();

                    /**
                     * 主表外键字段的值
                     */
                    Object keyValue = model.getFieldValue(masterField.getFieldName());

                    List<IFilter> filters = new ArrayList<IFilter>();
                    filters.add(new FilterSingle(slaveField, WhereFilterType.EQUAL, keyValue));
                    /**
                     * 获取从表数据
                     */
                    slaveModels = (List<IModel>) getModelList(slaveField.getModelClass(), filters,null).getData();

                    /**
                     * 如果需要缓存，则缓存
                     */
                    if (relation.isPrimaryRelationCache()) {
                        ormCache.cacheRelationModels(model, relation, slaveModels);
                    }

                    /**
                     * 根据一对多和多对多分别处理
                     */
                    if (!relation.isManyToMany()) {
                        relationModelsMap.put(relation.getSlaveField().getTableName(), slaveModels);
                    }
                    else {
                        /**
                         * 中间的关联表，也要放进去
                         */
                        relationModelsMap.put(relation.getSlaveField().getTableName(), slaveModels);
                        List<IModel> m2mMasterModels = getM2mMasterModel(model, relation,
                                                                         slaveModels);
                        relationModelsMap.put(relation.getM2mMasterField().getTableName(),
                                              m2mMasterModels);
                    }
                }
                else {
                    /**
                     * 根据一对多和多对多分别处理
                     */
                    if (!relation.isManyToMany()) {
                        List<IModel> slaveModelsSource = new ArrayList<>();
                        /**
                         * 再获取一次，确保数据一致，如果从表model没有设置缓存，可能会影响性能
                         */
                        for (IModel slaveModel : slaveModels) {
                            slaveModel = getModel(slaveModel, false);
                            slaveModelsSource.add(slaveModel);
                        }
                        relationModelsMap.put(relation.getSlaveField().getTableName(),
                                              slaveModelsSource);
                    }
                    else {
                        /**
                         * 中间的关联表，也要放进去
                         */
                        relationModelsMap.put(relation.getSlaveField().getTableName(), slaveModels);
                        List<IModel> m2mMasterModels = getM2mMasterModel(model, relation,
                                                                         slaveModels);
                        relationModelsMap.put(relation.getM2mMasterField().getTableName(),
                                              m2mMasterModels);
                    }
                }
            }

            return relationModelsMap;

        }
        catch (Exception e) {
            throw new MsgException("获取关联model出错", e);
        }
    }


    /**
     * 获取多对多右端主表数据
     *
     * @param masterModel 多对多左端主表
     * @param relation 关联信息
     * @param middleSlaveModels 中间表数据
     */
    private List<IModel> getM2mMasterModel(IModel masterModel, IRelation relation,
        List<IModel> middleSlaveModels) throws InstantiationException, IllegalAccessException {
        /**
         * 主表外键字段
         */
        IField masterField = relation.getMasterField();
        /**
         * 从表关联字段
         */
        IField slaveField = relation.getSlaveField();

        /**
         * 主表外键字段的值
         */
        Object keyValue = masterModel.getFieldValue(masterField.getFieldName());
        /**
         * 如果是多对多关联，则进行下一步处理
         */
        IField m2mMasterField = relation.getM2mMasterField();
        IField m2mslaveField = relation.getM2mSlaveField();
        /**
         * 存放多对多主表结果
         */
        List<IModel> m2mMasterModels = new ArrayList<>();
        /**
         * 仅用于判断多对多主表是否用了缓存
         */
        IModel m2mMasterModel = m2mMasterField.getModelClass().newInstance();

        /**
         * 如果多对多主表使用了缓存
         */
        if (m2mMasterModel.isNeedCache()) {
            for (IModel slaveModel : middleSlaveModels) {
                /**
                 * 从表中，和多对多主表关联的字段的值
                 */
                Object m2mSlaveFkValue = slaveModel.getFieldValue(m2mslaveField.getFieldName());
                IModel m2mMasterModel1 = m2mMasterField.getModelClass().newInstance();
                m2mMasterModel1.setFieldValue(m2mMasterField.getFieldName(), m2mSlaveFkValue);
                /**
                 * 如果m2mslaveField不是主键，就必须明确是唯一的，这一点，在IRelation的实现中体现
                 */
                m2mMasterModel1 = getModel(m2mMasterModel1, false);

                m2mMasterModels.add(m2mMasterModel1);
            }
        }
        else {
            /**
             * 否则通过sql获取
             */
            String sql = " SELECT " + m2mMasterField.getTableName() + ".* FROM " + m2mMasterField
                .getTableName() + " LEFT JOIN " + m2mslaveField.getTableName() + " ON "
                + m2mslaveField.getFieldName() + " = " + m2mMasterField.getTableName() + "."
                + m2mMasterField.getFieldName() + " WHERE " + m2mslaveField.getTableName() + "."
                + slaveField.getFieldName() + "=@" + slaveField.getFieldName();
            SQLParameterList pc = new SQLParameterList();
            /**
             * 注意，这里的keyValue确实是model的外联字段值
             */
            pc.put(slaveField.getFieldName(), keyValue);

            m2mMasterModels = getModelListSQL(sql, pc, m2mMasterField.getModelClass());
        }

        return m2mMasterModels;
    }

    /**
     * 把关联表数据入库 gislin 2017年7月19日
     */
    private <T extends IModel> void updateRelationModels(T model) {

        List<IRelation> relations = model.getRelations();

        /**
         * 如果没有关联关系设置，则不处理
         */
        if (relations == null || relations.size() == 0) {
            return;
        }

        /**
         * 判断需要更新的model中是否有关联关系的数据，如果没有，说明不需要更新
         */
        boolean hashRelationsData = false;

        for (IRelation relation : relations) {

            /**
             * 从表关联字段
             */
            IField slaveField = relation.getSlaveField();

            List<IModel> slaveModels = model.getRelationModel(slaveField.getTableName());

            /**
             * 如果关联数据为空，则不做处理
             */
            if (slaveModels != null) {
                hashRelationsData = true;
                break;
            }
        }

        if (!hashRelationsData) {
            return;
        }

        try {
            dataExecuter.beginTransaction();

            /**
             * 再获取一次model，因为有些情况下，关联表所需要的外键值并没有传进来
             */
            T dataSourceModel = getModelWithOutRelation(model);

            for (IRelation relation : relations) {

                /**
                 * 主表外键字段
                 */
                IField masterField = relation.getMasterField();
                /**
                 * 从表关联字段
                 */
                IField slaveField = relation.getSlaveField();

                List<IModel> slaveModels = model.getRelationModel(slaveField.getTableName());

                /**
                 * 如果关联数据为空，则不做处理
                 */
                if (slaveModels == null) {
                    continue;
                }

                /**
                 * 主表外键字段的值
                 */
                Object keyValue = dataSourceModel.getFieldValue(masterField.getFieldName());

                /**
                 * 重新设置下外键的值，确保可以关联上，避免业务层忘记这一步
                 */
                for (IModel slaveModel : slaveModels) {
                    slaveModel.setFieldValue(slaveField.getFieldName(), keyValue);
                }

                /**
                 * 删除关联关系
                 */
                if (relation.isPrimaryRelationCache()) {
                    ormCache.deleteRelationCacheModels(model, relation);
                }

                /**
                 * 如果是多对多关联就简单处理，先删除后插入
                 */
                if (relation.isManyToMany()) {
                    String deleteSQL =
                        " DELETE FROM " + slaveField.getTableName() + " WHERE " + slaveField
                            .getFieldName() + "=@" + slaveField.getFieldName();
                    SQLParameterList pc = new SQLParameterList();
                    pc.put(slaveField.getFieldName(), keyValue);
                    dataExecuter.delete(deleteSQL, pc);

                    creates(slaveModels);
                }
                else {
                    /**
                     * 如果是一对多，需要谨慎处理，因为从表可能会被其他业务所使用，不能简单先删除后插入.
                     * 先把从表中相关记录的外键置空，使其脱离关联，再把新的关联更新入库。
                     */
                    String updateSlaveSQL =
                        " UPDATE " + slaveField.getTableName() + " SET " + slaveField.getFieldName()
                            + " = null WHERE " + slaveField.getFieldName() + "=@" + slaveField
                            .getFieldName();
                    SQLParameterList pc = new SQLParameterList();
                    pc.put(slaveField.getFieldName(), keyValue);
                    dataExecuter.update(updateSlaveSQL, pc);

                    for (IModel slaveModel : slaveModels) {
                        /**
                         * 删除从表缓存
                         */
                        if (slaveModel.isNeedCache()) {
                            ormCache.deleteModelCache(slaveModel);
                        }

                        try {
                            checkModelPrimarykeyIsMissOrEmpty(slaveModel);
                        }
                        catch (PrimarykeyMissException e) {
                            throw e;
                        }
                        catch (PrimarykeyEmptyException e) {
                            /**
                             * 如果主键为null，认为是要新增
                             */
                            create(slaveModel);
                            continue;
                        }

                        if (getModel(slaveModel) == null) {
                            /**
                             * 如果库里不存在，认为是要新增
                             */
                            create(slaveModel);
                        }
                        else {
                            /**
                             * 如果既有主键值，在库里又存在，那么就更新
                             */
                            update(slaveModel);
                        }
                    }
                }
            }

            dataExecuter.commit();
        }
        catch (Exception e) {
            dataExecuter.rollback();
            throw e;
        }
    }

    /**
     * 更新纯model，不包含关联model gislin 2017年7月28日
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private <T extends IModel> void updatePureModel(T model) {

        if (model == null) {
            return;
        }

        /**
         * 主键不能为空
         */
        checkModelPrimarykeyIsMissOrEmpty(model);

        IModelAddition modelAddition = model.getModelAddition();

        if (modelAddition != null) {
            modelAddition.beforeUpdate(model);
        }

        List<IField> keyField = model.getKeyFields();

        List<IField> fields = model.getFields();

        StringBuffer whereStrBuffer = new StringBuffer(" WHERE ");
        StringBuffer updateStrBuffer = new StringBuffer(
            " UPDATE " + model.getTableName() + " SET ");
        SQLParameterList parameters = model.toSQLParameter(null);

        for (int i = 0; i < keyField.size(); i++) {

            IField field = keyField.get(i);

            whereStrBuffer.append(getFieldNameForSQL(field) + " = @" + field.getFieldName());

            if (i < keyField.size() - 1) {
                whereStrBuffer.append(" AND ");
            }
        }

        for (IField field : fields) {

            if (!field.isForUpdate()) {
                continue;
            }

            Object value = model.getFieldValue(field.getFieldName());

            if (value == null) {
                continue;
            }
            /**
             * 如果是nullValue，则使用置空处理
             */
            if (value.equals(FieldValue.nullValue)) {
                updateStrBuffer.append(field.getFieldName() + "=null,");
            }
            else {
                updateStrBuffer.append(field.getFieldName() + "=@" + field.getFieldName() + ",");
            }
        }
        updateStrBuffer.replace(updateStrBuffer.length() - 1, updateStrBuffer.length(), " ");

        dataExecuter.update(updateStrBuffer.toString() + whereStrBuffer.toString(), parameters);

        if (model.isNeedCache()) {
            ormCache.deleteModelCache(model);
        }

    }

    /**
     * 获取本model的值，不包含关联关系 gislin 2017年7月19日
     *
     * @param isConverDomain 是否翻译域信息
     */
    @SuppressWarnings("unchecked")
    private <T extends IModel> T getPureModel(T model, boolean isConverDomain) {

        T resultModels = null;

        /**
         * 先检查缓存中是否有数据，有的话，就直接返回，这里不需要进行主键判断，因为缓存的key可能不需要主键
         */
        if (model.isNeedCache()) {
            resultModels = (T) ormCache.getModel(model);
        }

        if (resultModels != null) {
            return resultModels;
        }

        List<Row> rows = null;

        /**
         * 先尝试根据主键获取数据
         */
        try {
            checkModelPrimarykeyIsMissOrEmpty(model);

            List<IField> keyField = model.getKeyFields();

            StringBuffer strBuffer = new StringBuffer();
            SQLParameterList parameters = new SQLParameterList();

            for (int i = 0; i < keyField.size(); i++) {

                IField field = keyField.get(i);
                Object value = model.getFieldValue(field.getFieldName());

                strBuffer.append(field.getFieldName() + " = @" + field.getFieldName());
                parameters.put(field.getFieldName(), value);

                if (i < keyField.size() - 1) {
                    strBuffer.append(" AND ");
                }
            }

            strBuffer.insert(0, " SELECT * FROM " + model.getTableName() + " WHERE ");

            rows = dataExecuter.queryList(strBuffer.toString(), parameters);

        }
        catch (Exception e) {

        }

        if (rows == null || rows.size() == 0) {
            /**
             * 再尝试根据唯一值获取数据
             */
            try {
                List<IField> uniquesField = model.getUniques();

                for (IField field : uniquesField) {
                    StringBuffer strBuffer = new StringBuffer();
                    SQLParameterList parameters = new SQLParameterList();

                    Object value = model.getFieldValue(field.getFieldName());

                    strBuffer.append(field.getFieldName() + " = @" + field.getFieldName());
                    parameters.put(field.getFieldName(), value);

                    strBuffer.insert(0, " SELECT * FROM " + model.getTableName() + " WHERE ");

                    rows = dataExecuter.queryList(strBuffer.toString(), parameters);

                    if (rows != null && rows.size() > 0) {
                        break;
                    }

                }

            }
            catch (Exception e) {

            }
        }

        if (rows == null || rows.size() == 0) {
            return null;
        }

        resultModels = (T) modelConver.converORMModel(rows.get(0), model.getClass());

        /**
         * 翻译域信息
         */
        if (isConverDomain) {
            resultModels = converDomainSingle(resultModels);
        }

        if (model.isNeedCache()) {
            ormCache.cacheModel(resultModels);
        }

        return resultModels;
    }

    /**
     * 把本model入库 gislin 2017年7月19日
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    private <T extends IModel> void createPureModel(T model) {

        if (model == null) {
            return;
        }

        try {
            dataExecuter.beginTransaction();

            IModelAddition modelAddition = model.getModelAddition();

            if (modelAddition != null) {
                modelAddition.beforeCreate(model);
            }

            SQLParameterList sqlParameters = buildCreateParameter(model);
            String createSQL = buildCreateSQL(model);

            dataExecuter.create(createSQL, sqlParameters);

            if(model.isNeedCache()){
                ormCache.deleteModelCache(model);
            }

            if (modelAddition != null) {
                modelAddition.afterCreate(model);
            }

            dataExecuter.commit();
        }
        catch (Exception e) {
            dataExecuter.rollback();
            throw e;
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    protected <T extends IModel> SQLParameterList buildCreateParameter(T model) {

        if (model == null) {
            return null;
        }

        IModelAddition modelAddition = model.getModelAddition();

        if (modelAddition != null) {
            modelAddition.generateDefaultValue(model);
        }

        /**
         * 这个检查要放在这里，因为上一句要生成默认值，有些场景也会生成主键，
         * 所以要在这个之后检查主键情况。
         */
        checkModelPrimarykeyIsMissOrEmpty(model);

        SQLParameterList parameters = model.toSQLParameter(null);

        return parameters;
    }

    protected <T extends IModel> String buildCreateSQL(T model) {

        if (model == null) {
            return null;
        }

        List<IField> fields = model.getFields();

        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer.append("INSERT INTO ");
        sqlBuffer.append(model.getTableName());
        sqlBuffer.append(" ( ");

        StringBuffer valueBuffer = new StringBuffer();

        valueBuffer.append(" ( ");

        for (int i = 0; i < fields.size(); i++) {

            IField field = fields.get(i);

            if (!field.isForUpdate()) {
                continue;
            }

            Object value = model.getFieldValue(field.getFieldName());

            if (value == null) {
                continue;
            }

            sqlBuffer.append(getFieldNameForSQL(field));
            valueBuffer.append("@" + field.getFieldName());

            sqlBuffer.append(",");
            valueBuffer.append(",");
        }

        sqlBuffer.deleteCharAt(sqlBuffer.lastIndexOf(","));
        valueBuffer.deleteCharAt(valueBuffer.lastIndexOf(","));

        sqlBuffer.append(" ) ");
        valueBuffer.append(" ) ");
        sqlBuffer.append(" VALUES ");

        String sql = sqlBuffer.toString() + valueBuffer.toString();

        return sql;

    }

    /**
     * 获取用于SQL上展示的字段名称
     * @param field
     * @return
     */
    public String getFieldNameForSQL(IField field ){
        return field.getFieldName();
    }

    /**
     * 翻译域信息,针对model gislin 2017年8月25日
     */
    private <T extends IModel> List<T> converDomainMulit(List<T> models) {
        if (models == null || models.size() == 0) {
            return models;
        }

        List<IDomainField> domainFields = models.get(0).getDomainFields();

        for (IDomainField domainField : domainFields) {

            for (IModel m : models) {
                Object domainPkValue = m.getFieldValue(domainField.getFieldName().getFieldName());

                if (domainPkValue == null) {
                    continue;
                }

                Object domainText = domainField.getDomainValue(domainPkValue, this);
                m.setFieldValue(domainField.getDomainFieldAliasName(), domainText);
            }
        }
        return models;
    }

    /**
     * 翻译域信息 gislin 2017年8月25日
     */
    private <T extends IModel> T converDomainSingle(T model) {
        if (model == null) {
            return model;
        }

        List<T> models = new ArrayList<>();
        models.add(model);

        models = converDomainMulit(models);

        if (models != null && models.size() > 0) {
            return models.get(0);
        }
        else {
            return null;
        }
    }

    /**
     * 检查model是否有主键字段
     */
    private void checkModelPrimarykeyIsMiss(IModel model) {
        List<IField> keyField = model.getKeyFields();

        if (keyField == null || keyField.size() == 0) {
            throw new PrimarykeyMissException(model, null);
        }
    }

    /**
     * 检查model是否有主键字段,主键是否为空
     */
    protected void checkModelPrimarykeyIsMissOrEmpty(IModel model) {

        checkModelPrimarykeyIsMiss(model);

        List<IField> keyField = model.getKeyFields();

        for (int i = 0; i < keyField.size(); i++) {

            IField field = keyField.get(i);
            Object value = model.getFieldValue(field.getFieldName());

            if (value == null) {
                throw new PrimarykeyEmptyException(model, null);
            }
        }
    }

    @Override
    public <T extends IModel> List<T> getModelListSQL(String sql, SQLParameterList pc,
        Class<T> modelClass) {

        List<Row> rows = dataExecuter.queryList(sql, pc);
        List<T> models = modelConver.converToORMModels(rows, modelClass);
        models = converDomainMulit(models);
        return models;
    }

    /**
     * 翻译域信息，针对row
     */
    @Override
    public <T extends IModel> List<Row> converDomain(List<Row> models, Class<T> modelClass) {
        if (models == null || models.size() == 0) {
            return models;
        }

        try {
            IModel modelInstance = modelClass.newInstance();

            List<IDomainField> domainFields = modelInstance.getDomainFields();

            for (IDomainField domainField : domainFields) {

                for (Row m : models) {
                    Object domainPkValue = m.get(domainField.getFieldName().getFieldName());

                    if (domainPkValue == null) {
                        continue;
                    }

                    Object domainText = domainField.getDomainValue(domainPkValue, this);
                    m.put(domainField.getDomainFieldAliasName(), domainText);
                }
            }
            return models;
        }
        catch (Exception e) {
            throw new MsgException("翻译域信息出错", e);
        }
    }

    @Override
    public void close() {
        dataExecuter.close();
    }
}
